/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.manage;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.repository.dal.evaluation.*;
import cn.ac.nercis.pes.repository.dal.project.SystemEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemProcessRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.dal.report.EvaluationReportRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.evaluation.ResultCorrectionDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.evaluation.UnitRiskAnalysisService;
import cn.ac.nercis.pes.service.project.SystemProcessService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 处理关联数据服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record ProcessRelatedService(UnitRiskAnalysisService unitRiskAnalysisService,
                                    UnitRiskAnalysisRepository riskAnalysisRepository,
                                    ResultCorrectionRepository resultCorrectionRepository,
                                    UnitEvaluationRepository unitEvaluationRepository,
                                    EvaluationRecordRepository evaluationRecordRepository,
                                    AssetEvaluationDescRepository evaluationDescRepository,
                                    AssetEvaluationMethodRepository evaluationMethodRepository,
                                    SystemProcessService systemProcessService,
                                    EvaluationReportRepository evaluationReportRepository,
                                    OverallEvaluationRepository overallEvaluationRepository,
                                    LayerEvaluationRepository layerEvaluationRepository,
                                    EvaluationRequireRepository evaluationRequireRepository,
                                    SecurityDocumentRepository securityDocumentRepository,
                                    PersonnelRepository personnelRepository,
                                    AssetKeyDataRepository assetKeyDataRepository, AssetRepository assetRepository,
                                    SecurityThreatRepository securityThreatRepository,
                                    PasswordServerRepository passwordServerRepository,
                                    EvaluationToolsRepository evaluationToolsRepository,
                                    EvaluatorRepository evaluatorRepository,
                                    EvaluationContentRepository evaluationContentRepository,
                                    EvaluationProcessRepository evaluationProcessRepository,
                                    SystemProcessRepository systemProcessRepository,
                                    SystemTargetRepository systemTargetRepository,
                                    SystemEvaluationRepository systemEvaluationRepository) {
    @Autowired
    public ProcessRelatedService {
    }

    /**
     * 重置单元指标风险分析
     * @param systemId 系统ID
     * @param unitTargetId 单元指标ID
     */
    public void resetRiskAnalysis(String systemId,String unitTargetId){
        unitRiskAnalysisService.resetRiskAnalysis(systemId,unitTargetId);
    }

    /**
     * 删除结果修正关联的数据
     *
     * @param entity 结果修正信息
     */
    public void deleteResultCorrectionRelatedData(ResultCorrectionDO entity) {
        resetRiskAnalysis(entity.getSystemId(),entity.getUnitTargetId());
    }

    /**
     * 重置结果修正关联的数据所属模块进度状态
     *
     * @param entity 结果修正信息
     */
    public void resetResultCorrectionRelatedModuleProcess(@NonNull ResultCorrectionDO entity) {
        resetResultCorrectionRelatedModuleProcess(entity.getSystemId(), findTargetName(entity.getSystemId(),entity.getLayerTargetId()));
    }

    /**
     * 删除测评纪录关联的数据
     *
     * @param entity 测评纪录信息
     */
    public void deleteEvalRecordRelatedData(EvaluationRecordDO entity) {
        var unitEvaluationDO = unitEvaluationRepository.getBySystemIdAndUnitTargetId(entity.getSystemId(),
                entity.getUnitTargetId());
        var resultCorrectionDO = resultCorrectionRepository.findBySystemIdAndRecord(entity.getSystemId(),
                EvaluationRecordDO.builder().id(entity.getId()).build());
        unitEvaluationDO.ifPresent(ue -> {
            ue.setFinishState(FinishState.INCOMPLETE);
            ue.setModifyTime(LocalDateTime.now());
            unitEvaluationRepository.save(ue);
        });
        if(CollectionUtils.isNotEmpty(resultCorrectionDO)){
            resultCorrectionRepository.deleteAll(resultCorrectionDO);
        }
    }

    /**
     * 重置测评记录关联的数据所属模块进度状态
     *
     * @param entity 测评纪录信息
     */
    public void resetEvalRecordRelateModuleProcess(@NonNull EvaluationRecordDO entity) {
        this.resetEvalRecordRelateModuleProcess(entity.getSystemId(), findTargetName(entity.getSystemId(),entity.getLayerTargetId()));
    }

    /**
     * 删除对象关联指标下所关联的数据
     *
     * @param deleteData 对象关联指标信息
     */
    public void deleteAssetRelateTargetRelatedData(List<AssetEvaluationMethodDO> deleteData) {
        Set<String> assetEvaMethodIds = deleteData.stream()
                .map(AssetEvaluationMethodDO::getId)
                .collect(Collectors.toSet());
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllByAssetEvaMethodIdIn(assetEvaMethodIds);
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        evaluationRecords.forEach(this::deleteEvalRecordRelatedData);
        evaluationRecordRepository.deleteAll(evaluationRecords);
    }

    /**
     * 重置对象关联指标下所关联的数据所属模块进度状态
     *
     * @param deleteData 对象关联指标信息
     */
    public void resetAssetRelateTargetRelateModuleProcess(List<AssetEvaluationMethodDO> deleteData) {
        Set<String> assetEvaMethodIds = deleteData.stream()
                .map(AssetEvaluationMethodDO::getId)
                .collect(Collectors.toSet());
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllByAssetEvaMethodIdIn(assetEvaMethodIds);
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        String systemId = deleteData.stream().map(AssetEvaluationMethodDO::getSystemId).findFirst().orElse("");
        deleteData.stream()
                .map(AssetEvaluationMethodDO::getLayerTargetId)
                .distinct()
                .map(id->findTargetName(systemId,id))
                .forEach(layerTargetName -> resetEvalRecordRelateModuleProcess(systemId, layerTargetName));
    }

    /**
     * 删除指标是否适用变更所影响的数据
     *
     * @param targetDO 指标信息
     */
    public void deleteTargetApplyRelatedData(@NonNull SystemTargetDO targetDO) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndUnitTargetId(targetDO.getSystemId(), targetDO.getKmTargetId());
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        evaluationRecords.forEach(this::deleteEvalRecordRelatedData);
        evaluationRecordRepository.deleteAll(evaluationRecords);
    }

    /**
     * 重置指标是否适用变更所影响的数据所属模块进度状态
     *
     * @param targetDO 指标信息
     */
    public void resetTargetApplyRelateModuleProcess(@NonNull SystemTargetDO targetDO) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndUnitTargetId(targetDO.getSystemId(), targetDO.getKmTargetId());
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        this.resetEvalRecordRelateModuleProcess(targetDO.getSystemId(), this.findTargetName(targetDO.getSystemId(),targetDO.getKmParentId()));
    }

    /**
     * 重置系统资产测评所影响的数据所属模块进度状态
     *
     * @param assetDO 系统资产信息
     */
    public void resetAssetEvalRelateModuleProcess(@NonNull AssetDO assetDO) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(assetDO.getSystemId(), assetDO);
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        evaluationRecords.stream()
                .map(EvaluationRecordDO::getLayerTargetId)
                .distinct()
                .map(id->findTargetName(assetDO.getSystemId(),id))
                .forEach(layerTargetName -> resetEvalRecordRelateModuleProcess(assetDO.getSystemId(), layerTargetName));
    }

    /**
     * 删除系统资产测评所影响的数据
     *
     * @param assetDO 系统资产信息
     */
    public void deleteAssetEvalRelateData(@NonNull AssetDO assetDO) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(assetDO.getSystemId(), assetDO);
        if (CollectionUtils.isEmpty(evaluationRecords)) {
            return;
        }
        evaluationRecords.forEach(this::deleteEvalRecordRelatedData);
        evaluationRecordRepository.deleteAll(evaluationRecords);
    }

    /**
     * 删除特殊指标关联的数据
     * @param systemId 系统Id
     * @param specialTargetId 特殊指标ID
     */
    public void deleteSpecialTargetRelatedData(String systemId,String specialTargetId){
        var evaluationMethods = evaluationMethodRepository.findBySystemIdAndUnitTargetId(systemId, specialTargetId);
        deleteAssetRelatedData(evaluationMethods);
    }

    /**
     * 删除对象关联的数据
     *
     * @param assetDO 资产对象信息
     */
    public void deleteAssetRelatedData(AssetDO assetDO) {
        List<AssetEvaluationMethodDO> evaluationMethods = evaluationMethodRepository.findAllBySystemIdAndAssetId(assetDO.getSystemId(), assetDO.getId());
        deleteAssetRelatedData(evaluationMethods);
        //删除对象关联的测评记录
        deleteAssetEvalRelateData(assetDO);
    }

    /**
     * 删除对象关联的数据
     *
     * @param evaluationMethods 对象关联指标的数据
     */
    public void deleteAssetRelatedData(List<AssetEvaluationMethodDO> evaluationMethods) {
        if (CollectionUtils.isEmpty(evaluationMethods)) {
            return;
        }
        this.deleteAssetRelateTargetRelatedData(evaluationMethods);
        evaluationMethodRepository.deleteAll(evaluationMethods);
    }

    /**
     * 删除重要数据对象关联的数据
     *
     * @param deleteAssets 关联的资产对象
     */
    public void deleteKeyDataRelatedData(List<AssetDO> deleteAssets) {
        deleteAssets.forEach(this::deleteAssetRelatedData);
    }

    /**
     * 删除系统关联的数据
     *
     * @param systemId 系统ID
     */
    public void deleteSystemRelatedData(String systemId) {
        this.evaluationReportRepository.deleteAllBySystemId(systemId);
        this.riskAnalysisRepository.deleteAllBySystemId(systemId);
        this.resultCorrectionRepository.deleteAllBySystemId(systemId);
        this.overallEvaluationRepository.deleteAllBySystemId(systemId);
        this.layerEvaluationRepository.deleteAllBySystemId(systemId);
        this.unitEvaluationRepository.deleteAllBySystemId(systemId);
        this.evaluationRecordRepository.deleteAllBySystemId(systemId);
        this.evaluationRequireRepository.deleteAllBySystemId(systemId);
        this.evaluationMethodRepository.deleteAllBySystemId(systemId);
        this.evaluationDescRepository.deleteAllBySystemId(systemId);
        this.securityDocumentRepository.deleteAllBySystemId(systemId);
        this.personnelRepository.deleteAllBySystemId(systemId);
        this.assetKeyDataRepository.deleteAllBySystemId(systemId);
        this.assetRepository.deleteAllBySystemId(systemId);
        this.securityThreatRepository.deleteAllBySystemId(systemId);
        this.passwordServerRepository.deleteAllBySystemId(systemId);
        this.evaluationToolsRepository.deleteAllBySystemId(systemId);
        this.evaluatorRepository.deleteAllBySystemId(systemId);
        this.evaluationContentRepository.deleteAllBySystemId(systemId);
        this.evaluationProcessRepository.deleteAllBySystemId(systemId);
        this.systemProcessRepository.deleteAllBySystemId(systemId);
        this.systemTargetRepository.deleteAllBySystemId(systemId);
        this.systemEvaluationRepository.deleteBySystemId(systemId);
    }

    private void resetResultCorrectionRelatedModuleProcess(String systemId, String layerTargetName) {
        //设置关联功能模块进度状态
        EvaluationModule evaluationModule = EvaluationModule.fromNameByRiskAnalysis(layerTargetName);
        systemProcessService.setSystemProcess(systemId, evaluationModule, FinishState.INCOMPLETE);
    }

    private void resetEvalRecordRelateModuleProcess(String systemId, String layerTargetName) {
        //设置关联功能模块进度状态
        EvaluationModule evaluationModule = EvaluationModule.fromNameByUnitEval(layerTargetName);
        systemProcessService.setSystemProcess(systemId, evaluationModule, FinishState.INCOMPLETE);
        evaluationModule = EvaluationModule.fromNameByOverallEval(layerTargetName);
        systemProcessService.setSystemProcess(systemId, evaluationModule, FinishState.INCOMPLETE);
        this.resetResultCorrectionRelatedModuleProcess(systemId, layerTargetName);
    }

    private String findTargetName(String systemId,String targetId) {
        return systemTargetRepository.findBySystemIdAndKmTargetId(systemId,targetId)
                .map(SystemTargetDO::getKmName)
                .orElse(StringUtils.EMPTY);
    }

}